1 module unde.games.dizzy.omega.dizzy;
2 
3 import derelict.opengl3.gl;
4 import derelict.sdl2.mixer;
5 import std.algorithm;
6 import std.conv;
7 import std.math;
8 import std.stdio;
9 import unde.games.collision_detector;
10 import unde.games.dizzy.omega.rope;
11 import unde.games.object;
12 import unde.games.renderer;
13 import unde.global_state;
14 
15 class Dizzy:LiveGameObject
16 {
17     float def_x, def_y, def_z;
18     float dx = 0.0, dy = 0.0;
19     float force_dx = 0.0;
20 
21     bool sounds = true;
22     
23     Mix_Chunk *step;
24     Mix_Chunk *jump_sound;
25 
26     this (MainGameObject root, float[3] coords)
27     {
28         def_x = x = coords[0];
29         def_y = y = coords[1];
30         def_z = z = coords[2];
31 
32         energy = 100.0;
33         lives = 3;
34 
35         models["dizzy"] = root.models["dizzy"];
36         models["dizzy-cosmonaut"] = root.models["dizzy-cosmonaut"];
37         if ("kitchen-knife" in root.models)
38             models["kitchen-knife"] = root.models["kitchen-knife"];
39         if ("branch" in root.models)
40             models["branch"] = root.models["branch"];
41         if ("bucket-water" in root.models)
42             models["bucket-water"] = root.models["bucket-water"];
43         if ("spade" in root.models)
44             models["spade"] = root.models["spade"];
45 
46         step = Mix_LoadWAV("sounds/step.wav");
47         if(!step) {
48             writefln("Mix_LoadWAV 'sounds/step.wav': %s", Mix_GetError().to!(string)());
49         }
50 
51         jump_sound = Mix_LoadWAV("sounds/jump.wav");
52         if(!jump_sound) {
53             writefln("Mix_LoadWAV 'sounds/jump.wav': %s", Mix_GetError().to!(string)());
54         }
55         
56         super(root);
57     }
58     
59     immutable float side_sensor_dx = 0.9;
60     immutable float[2] side_sensor_y = [0.7, 2.5];
61     
62     immutable float top_sensor_dx = 0.8;
63     immutable float[2] top_sensor_y = [2.0, 2.8];
64     immutable float water_sensor_dx = 0.3;
65     immutable float[2] water_sensor_y = [1.1, 1.48];
66     
67     immutable float bottom_sensor_dx = 0.7;
68     immutable float bottom_sensor_dy = 1.0;
69 
70     bool jump;
71     long stunning;
72 
73     enum {
74         SPEED = 0.1,
75         JUMP_LEN = 8.0,
76         JUMP_H = 3.0,
77 
78         G = 8*JUMP_H*SPEED^^2 / JUMP_LEN^^2,
79         JUMP_V = sqrt(JUMP_H*2*G),
80         
81         ROTATE_SPEED = 360/(JUMP_V/G),
82 
83         MARS_JUMP_LEN = 10.0,
84         MARS_JUMP_H = 4.0,
85 
86         MARS_G = 8*MARS_JUMP_H*SPEED^^2 / MARS_JUMP_LEN^^2,
87         MARS_JUMP_V = sqrt(MARS_JUMP_H*2*MARS_G),
88     }
89 
90     Rope rope;
91     float[3] last_safe;
92     bool fall = false;
93     bool show_sensors;
94     
95     private:
96     bool cosm = true;
97     bool underwater = false;
98     bool on_ice = false;
99     float ice_start_x;
100     bool search_surface;
101 
102     string damaged_by;
103 
104     enum STATE
105     {
106         NO_ANIM,
107         DIE_ANIM,
108         USE_KNIFE,
109         CUT_ROPE,
110         THROW_BRANCH,
111         WATER,
112         DIG,
113     }
114 
115     bool dizzy_stay_anim(GlobalState gs, string name)
116     {
117         float f = (gframe*2)%120;
118         float degree = 0.0;
119         float translate = 0.0;
120         if (f < 60.0)
121         {
122             degree = f - 30.0;
123             translate = -0.1 + 0.2*f/60.0;
124         }
125         else if (f < 120.0)
126         { 
127             degree = 30.0 - (f - 60.0);
128             translate = 0.1 - 0.2*(f - 60.0)/60.0;
129         }
130         
131         if (name == "Left_Glove")
132         {
133             glTranslatef(0.8, 1.4, 0.0);
134             glRotatef(-degree, 0.0, 0.0, 1.0);
135             glTranslatef(-0.8, -1.4, 0.0);
136         }
137         
138         if (name == "Right_Glove")
139         {
140             glTranslatef(-0.8, 1.4, 0.0);
141             glRotatef(degree, 0.0, 0.0, 1.0);
142             glTranslatef(0.8, -1.4, 0.0);
143         }
144         
145         if (name == "Dizzy")
146         {
147             glTranslatef(0.0, translate, 0.0);
148         }
149 
150         return true;
151     }
152     
153     bool dizzy_walk_anim(GlobalState gs, string name)
154     {
155         float f = (gframe*4)%120;
156         float f2 = (gframe*4)%240;
157         float degree = 0.0;
158         float translate = 0.0;
159         
160         if (f < 60.0)
161             translate = -0.1 + 0.2*f/60.0;
162         else if (f < 120.0)
163             translate = 0.1 - 0.2*(f - 60.0)/60.0;
164 
165         if (f2 < 120.0)
166             degree = f2/2 - 30.0;
167         else if (f2 < 240.0)
168             degree = degree = 30.0 - (f2/2 - 60.0);
169         
170         if (name == "Left_Glove")
171         {
172             glTranslatef(0.8, 1.4, 0.0);
173             glTranslatef(0.0, 0.5, 0.0);
174             glRotatef(degree, 1.0, 0.0, 0.0);
175             glTranslatef(0.0, -0.5, 0.0);
176             glRotatef(-30, 0.0, 0.0, 1.0);
177             glRotatef(-90, 1.0, 0.0, 0.0);
178             glTranslatef(-0.8, -1.4, 0.0);
179         }
180         
181         if (name == "Right_Glove")
182         {
183             glTranslatef(-0.8, 1.4, 0.0);
184             glTranslatef(0.0, 0.5, 0.0);
185             glRotatef(-degree, 1.0, 0.0, 0.0);
186             glTranslatef(0.0, -0.5, 0.0);
187             glRotatef(30, 0.0, 0.0, 1.0);
188             glRotatef(-90, 1.0, 0.0, 0.0);
189             glTranslatef(0.8, -1.4, 0.0);
190         }
191 
192         if (name == "Left_Boot")
193         {
194             glTranslatef(0.0, 1.0, 0.0);
195             glRotatef(-degree, 1.0, 0.0, 0.0);
196             glTranslatef(0.0, -1.0, 0.0);
197         }
198 
199         if (name == "Right_Boot")
200         {
201             glTranslatef(0.0, 1.2, 0.0);
202             glRotatef(degree, 1.0, 0.0, 0.0);
203             glTranslatef(0.0, -1.2, 0.0);
204         }
205         
206         if (name == "Dizzy")
207         {
208             glTranslatef(0.0, translate, 0.0);
209         }
210 
211         return true;
212     }
213 
214     bool dizzy_jump_anim(GlobalState gs, string name)
215     {
216         float f = (frame*ROTATE_SPEED)%360;
217         glTranslatef(0.0, 1.4, 0.0);
218         glRotatef(-f, 1.0, 0.0, 0.0);
219         glTranslatef(0.0, -1.4, 0.0);
220         return true;
221     }
222 
223     bool dizzy_die_anim(GlobalState gs, string name)
224     {
225         float f = frame*ROTATE_SPEED;
226         if (f > 90.0) f = 90.0;
227         glTranslatef(0.0, 1.4 - (1.4-1.0)*f/45.0, 0.0);
228         glRotatef(f, 1.0, 0.0, 0.0);
229         glTranslatef(0.0, -1.4, 0.0);
230         return true;
231     }
232 
233     bool dizzy_cosmonaut_stay_anim(GlobalState gs, string name)
234     {
235         float f = (gframe*2)%120;
236         float degree = 0.0;
237         float translate = 0.0;
238         if (f < 60.0)
239         {
240             degree = f;
241             translate = -0.1 + 0.2*f/60.0;
242         }
243         else if (f < 120.0)
244         {
245             degree = 60.0 - (f - 60.0);
246             translate = 0.1 - 0.2*(f - 60.0)/60.0;
247         }
248         
249         if (name == "Left_Hand")
250         {
251             glTranslatef(0.5, 1.4, 0.0);
252             glRotatef(60.0-degree, 0.0, 0.0, 1.0);
253             glTranslatef(-0.5, -1.4, 0.0);
254         }
255         
256         if (name == "Right_Hand")
257         {
258             glTranslatef(-0.5, 1.4, 0.0);
259             glRotatef(-(60.0-degree), 0.0, 0.0, 1.0);
260             glTranslatef(0.5, -1.4, 0.0);
261         }
262         
263         if (name == "DizzyCosm")
264         {
265             glTranslatef(0.0, translate, 0.0);
266         }
267 
268         if (name == "Left_Fire" || name == "Right_Fire")
269         {
270             return false;
271         }
272 
273         return true;
274     }
275 
276     bool dizzy_cosmonaut_walk_anim(GlobalState gs, string name)
277     {
278         float f = (gframe*4)%120;
279         float f2 = (gframe*4)%240;
280         float degree = 0.0;
281         float translate = 0.0;
282         
283         if (on_ice && !(root.keys & (LEFT_KEY | RIGHT_KEY)))
284         {
285             f = 60;
286             f2= 60;
287         }
288         
289         if (f < 60.0)
290             translate = -0.1 + 0.2*f/60.0;
291         else if (f < 120.0)
292             translate = 0.1 - 0.2*(f - 60.0)/60.0;
293 
294         if (f2 < 120.0)
295             degree = f2/2 - 30.0;
296         else if (f2 < 240.0)
297             degree = degree = 30.0 - (f2/2 - 60.0);
298         
299         if (name == "Left_Hand")
300         {
301             glTranslatef(0.0, 1.5, 0.0);
302             glRotatef(degree, 1.0, 0.0, 0.0);
303             glTranslatef(0.0, -1.5, 0.0);
304         }
305         
306         if (name == "Right_Hand")
307         {
308             glTranslatef(0.0, 1.5, 0.0);
309             glRotatef(-degree, 1.0, 0.0, 0.0);
310             glTranslatef(0.0, -1.5, 0.0);
311         }
312 
313         if (name == "Left_Leg")
314         {
315             glTranslatef(0.0, 1.1, 0.0);
316             glRotatef(-degree, 1.0, 0.0, 0.0);
317             glTranslatef(0.0, -1.1, 0.0);
318         }
319 
320         if (name == "Right_Leg")
321         {
322             glTranslatef(0.0, 1.1, 0.0);
323             glRotatef(degree, 1.0, 0.0, 0.0);
324             glTranslatef(0.0, -1.1, 0.0);
325         }
326         
327         if (name == "DizzyCosm")
328         {
329             glTranslatef(0.0, translate, 0.0);
330         }
331 
332         if (name == "Left_Fire" || name == "Right_Fire")
333         {
334             return false;
335         }
336 
337         return true;
338     }
339 
340     bool dizzy_cosmonaut_jump_anim(GlobalState gs, string name)
341     {
342         glTranslatef(0.0, 1.4, 0.0);
343         glRotatef(-20.0, 1.0, 0.0, 0.0);
344         glTranslatef(0.0, -1.4, 0.0);
345         return true;
346     }
347 
348     bool dizzy_cosmonaut_die_anim(GlobalState gs, string name)
349     {
350         float f = frame*ROTATE_SPEED;
351         if (f > 45.0) f = 45.0;
352         glTranslatef(0.0, 1.4 - (1.4-1.0)*f/45.0, 0.0);
353         glRotatef(f, 1.0, 0.0, 0.0);
354         glTranslatef(0.0, -1.4, 0.0);
355 
356         if (name == "Left_Fire" || name == "Right_Fire")
357         {
358             return false;
359         }
360         
361         return true;
362     }
363 
364     bool dizzy_cosmonaut_fall_anim(GlobalState gs, string name, ulong frame)
365     {
366         float f = frame;
367         float f2 = f;
368         if (f2 > 50.0) f2 = 50.0;
369 
370         glTranslatef(0.0, -0.8*f2/50.0, 0.0);
371 
372         if (name == "Right_Hand")
373         {
374             glTranslatef(-0.55, 1.33, 0.0);
375             glRotatef(-90.0*f2/50.0, 0.0, 0.0, 1.0);
376             glTranslatef(0.55, -1.33, 0.0);
377         }
378 
379         if (name == "Left_Leg")
380         {
381             glTranslatef(0.0, 1.2 - 0.2*f2/50.0, 0.0);
382             glRotatef(90.0*f2/50.0, 1.0, 0.0, 0.0);
383             glTranslatef(0.0, -1.2, 0.0);
384         }
385 
386         if (name == "Right_Leg")
387         {
388             glTranslatef(0.0, 1.2 - 0.2*f2/50.0, 0.0);
389             glRotatef(90.0*f2/50.0, 1.0, 0.0, 0.0);
390             glTranslatef(0.0, -1.2, 0.0);
391         }
392         
393         if (name == "DizzyCosm")
394         {
395             glTranslatef(0.0, 1.9, 0.0);
396             glRotatef(15.0*sin(f/50.0), 0.0, 1.0, 0.0);
397             glTranslatef(0.0, -1.9, 0.0);
398         }
399 
400         if (name == "Left_Fire" || name == "Right_Fire")
401         {
402             return false;
403         }
404         
405         return true;
406     }
407 
408     bool dizzy_cosmonaut_fall_anim(GlobalState gs, string name)
409     {
410         return dizzy_cosmonaut_fall_anim(gs, name, frame);
411     }
412 
413     bool dizzy_cosmonaut_on_rope_anim(GlobalState gs, string name)
414     {
415         float f = (frame*4)%120;
416         float f2 = (frame*4)%240;
417         float degree = 0.0;
418         float translate = 0.0;
419         
420         if (f < 60.0)
421             translate = -0.1 + 0.2*f/60.0;
422         else if (f < 120.0)
423             translate = 0.1 - 0.2*(f - 60.0)/60.0;
424 
425         if (f2 < 120.0)
426             degree = f2/2 - 30.0;
427         else if (f2 < 240.0)
428             degree = degree = 30.0 - (f2/2 - 60.0);
429         
430         if (name == "Left_Hand")
431         {
432             glTranslatef(1.1, 1.5, 0.0);
433             glRotatef(56.0, 0.0, 1.0, 0.0);
434             glTranslatef(-0.8, -1.5, 0.0);
435 
436             glTranslatef(0.0, 1.5, 0.0);
437             glRotatef(90.0+degree, 1.0, 0.0, 0.0);
438             glTranslatef(0.0, -1.5, 0.0);
439         }
440         
441         if (name == "Right_Hand")
442         {
443             glTranslatef(-1.1, 1.5, -0.3);
444             glRotatef(-56.0, 0.0, 1.0, 0.0);
445             glTranslatef(0.8, -1.5, 0.0);
446 
447             glTranslatef(0.0, 1.5, 0.0);
448             glRotatef(90.0-degree, 1.0, 0.0, 0.0);
449             glTranslatef(0.0, -1.5, 0.0);
450         }
451 
452         if (name == "Left_Leg")
453         {
454             glTranslatef(0.0, 1.1+translate, 0.0);
455             glRotatef(26.0, 0.0, 1.0, 0.0);
456             glTranslatef(0.0, -1.1, 0.0);
457         }
458 
459         if (name == "Right_Leg")
460         {
461             glTranslatef(0.0, 1.1+translate, 0.0);
462             glRotatef(-54.0, 0.0, 1.0, 0.0);
463             glTranslatef(0.0, -1.1, 0.0);
464         }
465 
466         if (name == "Left_Fire" || name == "Right_Fire")
467         {
468             return false;
469         }
470 
471         return true;
472     }
473     
474     string underwater_anim(GlobalState gs, string name)
475     {
476         if (name == "dizzy/dizzy.png")
477             return "dizzy/dizzy-underwater.png";
478         return name;
479     }
480 
481     bool underwater_jump_anim(GlobalState gs, string name)
482     {
483         float f = sin(cast(float)frame/25)*30;
484         glTranslatef(0.0, 1.4, 0.0);
485         glRotatef(-f, 1.0, 0.0, 0.0);
486         glTranslatef(0.0, -1.4, 0.0);
487         
488         if (name == "Left_Fire" || name == "Right_Fire")
489         {
490             return false;
491         }
492 
493         return true;
494     }
495     
496     bool dizzy_used_knife_anim(GlobalState gs, string name)
497     {
498         float f = frame;
499 
500         glRotatef(-130,0,1,0);
501         
502         if (f < 100.0)
503         {
504             if (name == "Left_Leg" || name == "Right_Leg")
505             {
506                 glTranslatef(0.0, 0.59*f/100.0, 0.51*f/100.0);
507                 glRotatef(-123.5*f/100.0, 1.0, 0.0, 0.0);
508             }
509     
510             if (name == "DizzyCosm" || name == "Left_Hand")
511             {
512                 glTranslatef(0.0, 0.0, 0.43*f/100.0);
513                 glRotatef(-40.0*f/100.0, 1.0, 0.0, 0.0);
514             }
515     
516             if (name == "Right_Hand")
517             {
518                 glTranslatef(-2.0*f/100.0, 0.2*f/100.0, 0.2*f/100.0);
519                 glRotatef(-109.2*f/100.0, 0.413*f/100.0, 0.882*f/100.0, 0.226*f/100.0);
520             }
521         }
522         else if (f < 400.0)
523         {
524             float s = (sin((f-100.0)/25.0)+1.0)/2.0;
525     
526             if (name == "Left_Leg" || name == "Right_Leg")
527             {
528                 glTranslatef(0.0, 0.59, 0.51);
529                 glRotatef(-123.5, 1.0, 0.0, 0.0);
530             }
531     
532             if (name == "DizzyCosm" || name == "Left_Hand")
533             {
534                 glTranslatef(0.0, 0.0, 0.43);
535                 glRotatef(-40.0, 1.0, 0.0, 0.0);
536             }
537     
538             if (name == "Right_Hand")
539             {
540                 glTranslatef(-2.0+0.2*s, 0.2-0.3*s, 0.2-0.5*s);
541                 glRotatef(-109.2-0.9*s, 0.413-0.199*s, 0.882+0.053*s, 0.226+0.055*s);
542     
543                 //glTranslatef(-1.8, -0.1, -0.3);
544                 //glRotatef(-110.1, 0.214, 0.936, 0.281);
545             }
546         }
547 
548         if (name == "Left_Fire" || name == "Right_Fire")
549         {
550             return false;
551         }
552         
553         return true;
554     }
555 
556     bool knife_used_by_dizzy_anim(GlobalState gs, string name)
557     {   
558         float f = frame;
559 
560         glRotatef(-130,0,1,0);
561 
562         if (f < 50.0)
563         {
564             glTranslatef(-1.2, 0.6, -0.3 - 1.0*f/50.0);
565             glRotatef(-92.9 + 37.9*f/50.0, 0.284 - 0.072*f/100.0, -0.868 + 0.167*f/100.0, 0.408 + 0.287*f/100.0);
566         }
567         else if (f < 100.0)
568         {
569             glTranslatef(-1.2 + 0.2*(f-50.0)/50.0, 0.6 - 0.4*(f-50.0)/50.0, -1.3 + 0.1*(f-50.0)/50.0);
570             glRotatef(-55.0 - 10.0*(f-50.0)/50.0, 0.212 - 0.032*(f-50.0)/50.0, -0.701 + 0.566*(f-50.0)/50.0, 0.681 + 0.293*(f-50.0)/50.0);
571         }
572         else if (f < 400.0)
573         {
574             float s = (sin((f-100.0)/25.0)+1.0)/2.0;
575             glTranslatef(-1.0+0.3*s, 0.2+0.1*s, -1.2-0.2*s);
576             glRotatef(-65.0+13.8*s, 0.180-0.262*s, -0.135+0.003*s, 0.974+0.014*s);
577         }
578         else return false;
579         
580         //glTranslatef(-0.7, 0.3, -1.4);
581         //glRotatef(-51.2, -0.082, -0.132, 0.988);
582 
583         glScalef(0.4, 0.4, 0.4);
584 
585         return true;
586     }
587 
588     bool dizzy_cuts_rope_anim(GlobalState gs, string name)
589     {
590         float f = frame;
591         glTranslatef(0, 0, 1.0);
592         glRotatef(-130,0,1,0);
593         
594         if (f < 50.0)
595         {
596             if (name == "Right_Hand")
597             {
598                 glTranslatef(-0.5, 1.5, -0.3);
599                 glRotatef(130.0*f/50.0, -1.0, -0.5, 0.0);
600                 glTranslatef(0.5, -1.5, 0.3);
601                 
602                 glTranslatef(0.0, 1.5, 0.0);
603                 glRotatef(180.0, 1.0, 0.0, 0.0);
604                 glTranslatef(0.0, -1.5, 0.0);
605             }
606         }
607 
608         if (name == "Left_Fire" || name == "Right_Fire")
609         {
610             return false;
611         }
612         
613         return true;
614     }
615 
616     bool knife_cuts_rope_anim(GlobalState gs, string name)
617     {
618         float f = frame;
619         glTranslatef(0, 0, 1.0);
620         glRotatef(-130,0,1,0);
621 
622         if (f < 50.0)
623         {
624             glTranslatef(-0.5, 1.5, -0.3);
625             glRotatef(130.0*f/50.0, -1.0, -0.5, 0.0);
626             glTranslatef(0.5, -1.5, 0.3);
627         }
628 
629         glTranslatef(-1.1, 1.9, -0.5);
630         glRotatef(90.0, 0.0, 1.0, 0.0);
631         glScalef(0.4, 0.4, 0.4);
632         
633         return true;
634     }
635 
636     bool dizzy_throw_branch_anim(GlobalState gs, string name)
637     {
638         float f = frame;
639         float degree = 0.0;
640 
641         glRotatef(-90,0,1,0);
642         
643         if (f < 45.0)
644             degree = f*2;
645         else if (f < 90.0)
646             degree = 90.0 - (f-45.0)*2;
647         
648         if (name == "Right_Hand")
649         {
650             glTranslatef(0.0, 1.9, 0.0);
651             glRotatef(degree, 1.0, 0.0, 0.0);
652             glTranslatef(0.0, -1.9, 0.0);
653         }
654         
655         if (name == "Left_Fire" || name == "Right_Fire")
656         {
657             return false;
658         }
659 
660         return true;
661     }
662 
663     bool branch_thrown_by_dizzy_anim(GlobalState gs, string name)
664     {
665         float f = frame;
666         
667         float degree = 0.0, degree2 = 0.0;
668         float dx = 0.0, dy = 0.0;
669         if (f < 45.0)
670             degree = f*2;
671         else return false;
672 
673         glRotatef(-90,0,1,0);
674 
675         glTranslatef(0.0, 1.9, 0.0);
676         glRotatef(degree, 1.0, 0.0, 0.0);
677         glTranslatef(0.0, -1.9, 0.0);
678         f = 0.0;
679         glTranslatef(-0.9, 0.5, -0.1);
680 
681         glRotatef(55.0, 0.0, 1.0, 0.0);
682         glRotatef(-60.0, 0.0, 0.0, 1.0);
683         glScalef(0.5, 0.5, 0.5);
684         
685         return true;
686     }
687 
688     bool dizzy_water_anim(GlobalState gs, string name)
689     {
690         glRotatef(-130.0, 0.0, 1.0, 0.0);
691         
692         if (name == "Left_Hand")
693         {
694             glTranslatef(-0.76, 0.54, -1.80);
695             glRotatef(148, 0.512, 0.781, 0.357);
696         }
697 
698         if (name == "Right_Hand")
699         {
700             glTranslatef(0.0, 0.34, -0.92);
701             glRotatef(38, 1.0, 0.0, 0.0);
702         }
703 
704         if (name == "Left_Fire" || name == "Right_Fire")
705         {
706             return false;
707         }
708         
709         return true;
710     }
711     
712     bool bucket_of_water_water_anim(GlobalState gs, string name)
713     {
714         glRotatef(-130.0, 0.0, 1.0, 0.0);
715 
716         glTranslatef(-1.1, 1.3, -0.54);
717         glRotatef(-85, 1.0, 0.0, 0.0);
718         glScalef(0.6, 0.6, 0.6);
719                 
720         return true;
721     }
722 
723     void trarot(float[][] pt, float[][] pr, int p1, int p2, float pos)
724     {
725         glTranslatef(pt[p1][0] + (pt[p2][0]-pt[p1][0])*pos,
726                      pt[p1][1] + (pt[p2][1]-pt[p1][1])*pos,
727                      pt[p1][2] + (pt[p2][2]-pt[p1][2])*pos);
728                      
729         glRotatef(pr[p1][0] + (pr[p2][0]-pr[p1][0])*pos,
730                   pr[p1][1] + (pr[p2][1]-pr[p1][1])*pos,
731                   pr[p1][2] + (pr[p2][2]-pr[p1][2])*pos,
732                   pr[p1][3] + (pr[p2][3]-pr[p1][3])*pos);
733     }
734 
735     bool dizzy_dig_anim(GlobalState gs, string name)
736     {
737         float f = frame%100.0;
738 
739         glRotatef(-90,0,1,0);
740 
741         // Left Hand Phases
742         float[][] lhpt = [[-0.34, 0.7, -0.37],
743                           [-0.34, 0.65, 0.0],
744                           [-0.54, 0.64, -0.48],];
745         float[][] lhpr = [[-123, -0.680, -0.719, 0.146],
746                           [-98, -0.521, -0.725, 0.450],
747                           [-82.9, -0.682, -0.620, 0.389],];
748                           
749         // Right Hand Phases
750         float[][] rhpt = [[0.26, 0.43, -0.17],
751                           [0.26, 0.52, 0.32],
752                           [0.58, 0.52, 0.21],];
753         float[][] rhpr = [[-131, -0.436, 0.871, -0.226],
754                           [-120, -0.327, 0.828, -0.455],
755                           [-120, -0.327, 0.828, -0.455],];
756 
757         // Right Leg Phases
758         float[][] rlpt = [[0.34, 0.44, -1.06],
759                           [0.33, -0.01, -1.40],
760                           [0.0, 0.0, -0.2],];
761         float[][] rlpr = [[37, 1.0, 0.0, 0.0],
762                           [37, 1.0, 0.0, 0.0],
763                           [0.0, 1.0, 0.0, 0.0]];
764         
765         if (f < 25.0)
766         {
767             if (name == "Left_Hand")
768             {    
769                 glTranslatef(lhpt[0][0], lhpt[0][1], lhpt[0][2]);
770                 glRotatef(lhpr[0][0], lhpr[0][1], lhpr[0][2], lhpr[0][3]);
771             }
772             else if (name == "Right_Hand")
773             {
774                 glTranslatef(rhpt[0][0], rhpt[0][1], rhpt[0][2]);
775                 glRotatef(rhpr[0][0], rhpr[0][1], rhpr[0][2], rhpr[0][3]);
776             }
777             else if (name == "Right_Leg")
778             {
779                 glTranslatef(rlpt[0][0]*f/25.0, rlpt[0][1]*f/25.0, rlpt[0][2]*f/25.0);
780                 glRotatef(rlpr[0][0]*f/25.0, rlpr[0][1], rlpr[0][2], rlpr[0][3]);
781             }
782         }
783         else if (f < 50.0)
784         {
785             if (name == "Left_Hand")
786             {
787                 trarot(lhpt, lhpr, 0, 1, (f-25.0)/25.0);
788             }
789             else if (name == "Right_Hand")
790             {
791                 trarot(rhpt, rhpr, 0, 1, (f-25.0)/25.0);
792             }
793             else if (name == "Right_Leg")
794             {
795                 trarot(rlpt, rlpr, 0, 1, (f-25.0)/25.0);
796             }
797             else
798             {
799                 glTranslatef(0.0, 0.0, -0.2*(f-25.0)/25.0);
800             }
801         }
802         else if (f < 75.0)
803         {
804             if (name == "Left_Hand")
805             {
806                 trarot(lhpt, lhpr, 1, 2, (f-50.0)/25.0);
807             }
808             else if (name == "Right_Hand")
809             {
810                 trarot(rhpt, rhpr, 1, 2, (f-50.0)/25.0);
811             }
812             else if (name == "Right_Leg")
813             {
814                 trarot(rlpt, rlpr, 1, 2, (f-50.0)/25.0);
815             }
816             else
817             {
818                 glTranslatef(0.0, 0.0, -0.2);
819             }
820         }
821         else
822         {
823             if (name == "Left_Hand")
824             {
825                 trarot(lhpt, lhpr, 2, 0, (f-75.0)/25.0);
826             }
827             else if (name == "Right_Hand")
828             {
829                 trarot(rhpt, rhpr, 2, 0, (f-75.0)/25.0);
830             }
831             else if (name == "Right_Leg")
832             {
833                 trarot(rlpt, rlpr, 2, 0, 0);
834             }
835             else
836             {
837                 glTranslatef(0.0, 0.0, -0.2);
838             }
839         }
840         
841         if (name == "Left_Fire" || name == "Right_Fire")
842         {
843             return false;
844         }
845 
846         return true;
847     }
848 
849     bool spade_dig_anim(GlobalState gs, string name)
850     {
851         float f = frame%100.0;
852 
853         // Phases
854         float[][] pt = [[-0.08, 0.08, -1.12],
855                         [-0.08, -0.42, -1.42],
856                         [1.17, 0.18, -2.07]];
857         float[][] pr = [[-61, -0.170, -0.884, 0.436],
858                         [-61, -0.170, -0.884, 0.436],
859                         [-42, -0.948, -0.110, -0.297]];
860 
861         glRotatef(-90,0,1,0);
862 
863         if (f < 25.0)
864         {
865             glTranslatef(pt[0][0], pt[0][1], pt[0][2]);
866             glRotatef(pr[0][0], pr[0][1], pr[0][2], pr[0][3]);
867         }
868         else if (f < 50.0)
869         {
870             trarot(pt, pr, 0, 1, (f-25.0)/25.0);
871         }
872         else if (f < 75.0)
873         {
874             trarot(pt, pr, 1, 2, (f-50.0)/25.0);
875         }
876         else
877         {
878             trarot(pt, pr, 2, 0, (f-75.0)/25.0);
879         }
880         
881         glScalef(0.8, 0.8, 0.8);
882         
883         return true;
884     }
885 
886     public:
887     override void draw(GlobalState gs)
888     {
889         glPushMatrix();
890         glTranslatef(x, y, z);
891 
892         //Dizzy
893         if (state == STATE.USE_KNIFE)
894         {
895             recursive_render(gs, models["dizzy-cosmonaut"], &dizzy_used_knife_anim);
896             recursive_render(gs, models["kitchen-knife"], &knife_used_by_dizzy_anim);
897         }
898         else if (state == STATE.CUT_ROPE)
899         {
900             recursive_render(gs, models["dizzy-cosmonaut"], &dizzy_cuts_rope_anim);
901             recursive_render(gs, models["kitchen-knife"], &knife_cuts_rope_anim);
902         }
903         else if (state == STATE.THROW_BRANCH)
904         {
905             recursive_render(gs, models["dizzy-cosmonaut"], &dizzy_throw_branch_anim);
906             recursive_render(gs, models["branch"], &branch_thrown_by_dizzy_anim);
907         }
908         else if (state == STATE.WATER)
909         {
910             recursive_render(gs, models["dizzy-cosmonaut"], &dizzy_water_anim);
911             recursive_render(gs, models["bucket-water"], &bucket_of_water_water_anim);
912         }
913         else if (state == STATE.DIG)
914         {
915             recursive_render(gs, models["dizzy-cosmonaut"], &dizzy_dig_anim);
916             recursive_render(gs, models["spade"], &spade_dig_anim);
917         }
918         else if (!cosm)
919         {            
920             if (state == STATE.DIE_ANIM)
921             {
922                 recursive_render(gs, models["dizzy"], &dizzy_die_anim);
923             }
924             else if (dx == 0.0)
925             {
926                 if (jump) recursive_render(gs, models["dizzy"],
927                     underwater ? &underwater_jump_anim: &dizzy_jump_anim,
928                     underwater ? &underwater_anim : null);
929                 else recursive_render(gs, models["dizzy"], &dizzy_stay_anim, underwater ? &underwater_anim : null);
930             }
931             else
932             {
933                 if (dx < 0) glRotatef(90,0,1,0);
934                 else if (dx > 0) glRotatef(-90,0,1,0);
935                 if (jump) recursive_render(gs, models["dizzy"],
936                     underwater ? &underwater_jump_anim: &dizzy_jump_anim, 
937                     underwater ? &underwater_anim : null);
938                 else recursive_render(gs, models["dizzy"],
939                     &dizzy_walk_anim, underwater ? &underwater_anim : null);
940             }
941         }
942         else
943         {
944             bool on_rope = rope !is null &&
945                 abs(rope.x - x) < 1.0 &&
946                 y < rope.rope[rope.static_segments][1] &&
947                 y > rope.rope[rope.cut_segm-1][1];
948 
949             if (state == STATE.DIE_ANIM)
950             {
951                 recursive_render(gs, models["dizzy-cosmonaut"],
952                     &dizzy_cosmonaut_die_anim);
953             }
954             else if (fall) recursive_render(gs, models["dizzy-cosmonaut"],
955                     &dizzy_cosmonaut_fall_anim);
956             else if (on_rope)
957             {
958                 if (x > rope.x) glRotatef(90,0,1,0);
959                 else if (x < rope.x) glRotatef(-90,0,1,0);
960 
961                 recursive_render(gs, models["dizzy-cosmonaut"],
962                     &dizzy_cosmonaut_on_rope_anim);
963             }
964             else if (dx == 0.0 && force_dx == 0.0)
965             {
966                 if (jump) recursive_render(gs, models["dizzy-cosmonaut"],
967                     underwater ? &underwater_jump_anim: &dizzy_cosmonaut_jump_anim);
968                 else recursive_render(gs, models["dizzy-cosmonaut"],
969                     &dizzy_cosmonaut_stay_anim);
970             }
971             else
972             {
973                 if (dx < 0 || force_dx > 0) glRotatef(90,0,1,0);
974                 else if (dx > 0 || force_dx < 0) glRotatef(-90,0,1,0);
975                 
976                 if (force_dx != 0.0)
977                 {
978                     glTranslatef(0.0, 1.4, 0.0);
979                     glRotatef(30.0, 1.0, 0.0, 0.0);
980                     glTranslatef(0.0, -1.4, 0.0);
981                 }
982 
983                 if (jump && force_dx == 0.0) recursive_render(gs, models["dizzy-cosmonaut"],
984                     underwater ? &underwater_jump_anim: &dizzy_cosmonaut_jump_anim);
985                 else recursive_render(gs, models["dizzy-cosmonaut"],
986                     &dizzy_cosmonaut_walk_anim);
987             }
988         }
989         glPopMatrix();
990 
991         if (show_sensors)
992         {
993             glEnable(GL_COLOR_MATERIAL);
994             glDisable(GL_LIGHTING);
995             glDisable(GL_CULL_FACE);
996             glBindTexture(GL_TEXTURE_2D, 0);
997             glColor4f(0.0, 0.0, 1.0, 0.5);
998             render_box([x-side_sensor_dx, y+side_sensor_y[0], z-side_sensor_dx, x, y+side_sensor_y[1], z+side_sensor_dx]);
999             render_box([x, y+side_sensor_y[0], z-side_sensor_dx, x+side_sensor_dx, y+side_sensor_y[1], z+side_sensor_dx]);
1000             glColor4f(0.0, 1.0, 0.0, 0.5);
1001             render_box([x-top_sensor_dx, y+top_sensor_y[0], z-top_sensor_dx, x+top_sensor_dx, y+top_sensor_y[1], z+top_sensor_dx]);
1002             glColor4f(0.0, 1.0, 1.0, 0.5);
1003             render_box([x-bottom_sensor_dx, y, z-bottom_sensor_dx, x+bottom_sensor_dx, y+bottom_sensor_dy, z+bottom_sensor_dx]);
1004             glColor4f(1.0, 1.0, 0.0, 0.5);
1005             render_box([x-water_sensor_dx, y+water_sensor_y[0], z-1.0, x+water_sensor_dx, y+water_sensor_y[1], z+1.0]);
1006             glColor4f(1.0, 1.0, 1.0, 1.0);
1007             glDisable(GL_COLOR_MATERIAL);
1008             glEnable(GL_LIGHTING);
1009             glEnable(GL_CULL_FACE);
1010         }
1011     }
1012 
1013     override bool tick(GlobalState gs)    
1014     {
1015         if (state == STATE.USE_KNIFE)
1016         {
1017             frame++;
1018             if (frame >= 400)
1019                 return false;
1020         }
1021         else if (state == STATE.CUT_ROPE)
1022         {
1023             frame++;
1024             if (frame >= 50)
1025                 return false;
1026         }
1027         else if (state == STATE.THROW_BRANCH)
1028         {
1029             frame++;
1030             if (frame >= 90)
1031                 return false;
1032         }
1033         else if (state == STATE.DIG)
1034         {
1035             frame++;
1036                         
1037             if (frame > 0 && frame%100 == 0)
1038             {
1039                 x += 0.5;
1040                 y -= 0.5;
1041             }
1042             if (frame >= 300)
1043                 return false;
1044         }
1045         else if (state == STATE.DIE_ANIM)
1046         {
1047             frame++;
1048         }
1049         else
1050         {
1051             if (stunning > 0)
1052                 stunning--;
1053             
1054             bool on_rope = rope !is null &&
1055                 abs(rope.x - x) < 1.0 &&
1056                 y < rope.rope[rope.static_segments][1] &&
1057                 y > rope.rope[rope.cut_segm-1][1];
1058 
1059             if (!on_rope)
1060             {
1061                 frame++;
1062                 if (root.keys & UP_KEY && !jump && !fall)
1063                 {
1064                     if (!cosm) dy = JUMP_V;
1065                     else dy = MARS_JUMP_V;
1066                     search_surface = false;
1067                     jump = true;
1068                     frame = 0;
1069 
1070                     if(sounds && cosm && !underwater && jump_sound && Mix_PlayChannel(0, jump_sound, -1)==-1)
1071                     {
1072                         writefln("Mix_PlayChannel jump_sound: %s\n",
1073                             Mix_GetError().to!(string)());
1074                     }
1075                 }
1076             }
1077             else
1078             {
1079                 if (jump)
1080                 {
1081                     jump = false;
1082                     if(sounds && cosm && jump_sound) Mix_HaltChannel(0);
1083                 }
1084                 
1085                 if (root.keys & UP_KEY && !(root.keys & DOWN_KEY))
1086                 {
1087                     frame++;
1088                     dy = SPEED;
1089                 }
1090                 else if (root.keys & DOWN_KEY && !(root.keys & UP_KEY))
1091                 {
1092                     frame--;
1093                     dy = -SPEED;
1094                 }
1095                 else
1096                     dy = 0.0;
1097             }
1098 
1099             float sdx = dx + force_dx;
1100 
1101             if (stunning > 0 && !jump)
1102             {
1103                 sdx *= sin(gframe/100.0f)*.5+1.0;
1104             }
1105 
1106             x += sdx;
1107             
1108             if (sdx < 0 && (if_intersect (root.collision_objects["solid"], [x-side_sensor_dx, y+side_sensor_y[0], z-side_sensor_dx, x, y+side_sensor_y[1], z+side_sensor_dx]) > 0 ||
1109                  on_ice && root.keys & LEFT_KEY && ice_start_x - x > 1.5))
1110             {
1111                 if (on_ice && root.keys & LEFT_KEY)
1112                     x -= sdx*0.9;
1113                 else
1114                     x -= sdx;
1115             }
1116             
1117             if (sdx > 0 && (if_intersect (root.collision_objects["solid"], [x, y+side_sensor_y[0], z-side_sensor_dx, x+side_sensor_dx, y+side_sensor_y[1], z+side_sensor_dx]) > 0 ||
1118                 on_ice && root.keys & RIGHT_KEY && x - ice_start_x > 1.5))
1119             {
1120                 if (on_ice && root.keys & RIGHT_KEY)
1121                     x -= sdx*0.9;
1122                 else
1123                     x -= sdx;
1124             }
1125         
1126             y += dy;
1127 
1128             if (dy > 0 && if_intersect (root.collision_objects["solid"], [x-top_sensor_dx, y+top_sensor_y[0], z-top_sensor_dx, x+top_sensor_dx, y+top_sensor_y[1], z+top_sensor_dx]) > 0)
1129             {
1130                 y -= dy;
1131             }
1132 
1133             Intersect in_water = if_intersect (root.collision_objects["water"], [x-water_sensor_dx, y+water_sensor_y[0], z-1.0, x+water_sensor_dx, y+water_sensor_y[1], z+1.0]);
1134             underwater = (in_water == Intersect.In);
1135 
1136             Intersect on_danger =
1137                 max(if_intersect (root.collision_objects["dangers"], [x-bottom_sensor_dx, y, z-bottom_sensor_dx, x+bottom_sensor_dx, y+bottom_sensor_dy, z+bottom_sensor_dx]),
1138                 if_intersect (root.collision_objects["dangers"], [x-side_sensor_dx, y+side_sensor_y[0], z-side_sensor_dx, x+side_sensor_dx, y+side_sensor_y[1], z+side_sensor_dx]),
1139                 if_intersect (root.collision_objects["dangers"], [x-top_sensor_dx, y+top_sensor_y[0], z-top_sensor_dx, x+top_sensor_dx, y+top_sensor_y[1], z+top_sensor_dx]));
1140 
1141             if (on_danger > 0 || damaged_by !is null)
1142             {
1143                 energy -= 1.0;
1144 
1145                 if ((last_intersect.startsWith("Acid") || 
1146                      last_intersect.startsWith("Lava")) &&
1147                     if_intersect (root.collision_objects["dangers"],
1148                         [x-side_sensor_dx, y, z-bottom_sensor_dx, x+side_sensor_dx,
1149                          y+top_sensor_y[1], z+bottom_sensor_dx]) == Intersect.In)
1150                 {
1151                     energy = 0.0;
1152                 }
1153 
1154                 if (last_intersect.startsWith("Pin"))
1155                 {
1156                     damaged_by = last_intersect;
1157                 }
1158                     
1159                 if (energy <= 0.0)
1160                 {
1161                     die(on_danger > 0 ? last_intersect : damaged_by);
1162                 }
1163             }
1164 
1165             Intersect on_the_ground =
1166                 if_intersect (root.collision_objects["solid"], [x-bottom_sensor_dx, y, z-bottom_sensor_dx, x+bottom_sensor_dx, y+bottom_sensor_dy, z+bottom_sensor_dx]);
1167 
1168             bool ice = stunning == 0 && (on_the_ground > 0 && last_intersect.startsWith("Ice.") ||
1169                     on_ice && on_the_ground == 0 && dy <= 0.0 && dy > -JUMP_V);
1170             if (on_ice && !ice)
1171             {
1172                 dx = 0.0;
1173                 if (root.keys & LEFT_KEY) dx -= SPEED;
1174                 if (root.keys & RIGHT_KEY) dx += SPEED;
1175             }
1176             
1177             if (ice && !on_ice)
1178             {
1179                 if (jump)
1180                 {
1181                     fall = true;
1182                     if (dx > 0.0) ice_start_x = x - 2.0;
1183                     else ice_start_x = x + 2.0;
1184                     dx = 0.0;
1185                 }
1186                 else ice_start_x = x;
1187                 frame = 0;
1188             }
1189 
1190             if (on_ice && abs(ice_start_x-x) < 1.5)
1191             {
1192                 frame = 0;
1193             }
1194 
1195             if (on_ice && !fall && frame > 100)
1196             {
1197                 fall = true;
1198                 frame = 0;
1199                 dx = 0.0;
1200             }
1201 
1202             if (fall && frame > 500)
1203             {
1204                 fall = false;
1205                 frame = 0;
1206                 if (root.keys & LEFT_KEY) dx -= SPEED;
1207                 if (root.keys & RIGHT_KEY) dx += SPEED;
1208             }
1209             
1210             on_ice = ice;
1211 
1212             if (!on_rope && !cosm ? dy > -JUMP_V : dy > -MARS_JUMP_V)
1213             {
1214                 if (!cosm) dy -= G;
1215                 else dy -= MARS_G;
1216             }
1217 
1218             Intersect on_cloud =
1219                 if_intersect (root.collision_objects["clouds"], [x-bottom_sensor_dx, y, z-bottom_sensor_dx, x+bottom_sensor_dx, y+0.1, z+bottom_sensor_dx]);
1220 
1221             if (on_cloud > 0)
1222             {
1223                 if (dy < -JUMP_V/15.0)
1224                     dy = -JUMP_V/15.0;
1225             }
1226 
1227             if (search_surface && on_the_ground == 0)
1228             {
1229                 dy = 0;
1230                 search_surface = false;
1231             }
1232 
1233             if (on_the_ground > 0 && dx != 0.0 && (gframe*4)%120 == 0)
1234             {
1235                 if(sounds && step && Mix_PlayChannel(1, step, 1)==-1)
1236                 {
1237                     writefln("Mix_PlayChannel step: %s\n",
1238                         Mix_GetError().to!(string)());
1239                 }
1240             }
1241             
1242             if ((on_the_ground > 0 || on_cloud > 0) && (dy <= 0 || search_surface))
1243             {
1244                 if (if_intersect (root.collision_objects["solid"], [x-bottom_sensor_dx, y+SPEED, z-bottom_sensor_dx, x+bottom_sensor_dx, y+SPEED+bottom_sensor_dy, z+bottom_sensor_dx]) > 0)
1245                 {
1246                     search_surface = true;
1247                     dy = SPEED;
1248                 }
1249                 else if (on_the_ground > 0) dy = 0;
1250 
1251                 float rotate = (frame*ROTATE_SPEED)%360;
1252                 if ((rotate < 15 || rotate > 345 || cosm || underwater))
1253                 {
1254                     force_dx = 0.0;
1255                     
1256                     bool bug_place = (x >= 239.1 && x <= 265.3 &&
1257                                       y >= -7.2  && y <= -4.4);
1258                     if (!on_danger && damaged_by is null && !underwater && !on_ice && !bug_place)
1259                     {
1260                         last_safe = [x, y, z];
1261                     }
1262                     if (jump)
1263                     {
1264                         if(sounds && cosm && jump_sound) Mix_HaltChannel(0);
1265 
1266                         jump = false;
1267                         if (!on_ice)
1268                         {
1269                             dx = 0;
1270                             if (root.keys & LEFT_KEY) dx -= SPEED;
1271                             if (root.keys & RIGHT_KEY) dx += SPEED;
1272                         }
1273                     }
1274                 }
1275             }
1276         }
1277         
1278         return true;
1279     }
1280 
1281     void use_knife()
1282     {
1283         state = STATE.USE_KNIFE;
1284         frame = 0;
1285     }
1286 
1287     void cut_rope()
1288     {
1289         state = STATE.CUT_ROPE;
1290         frame = 0;
1291     }
1292 
1293     void throw_branch()
1294     {
1295         state = STATE.THROW_BRANCH;
1296         frame = 0;
1297     }
1298 
1299     void water_with_bucket_of_water()
1300     {
1301         state = STATE.WATER;
1302         frame = 0;
1303     }
1304 
1305     void dig()
1306     {
1307         state = STATE.DIG;
1308         frame = 0;
1309     }
1310 
1311     void start_fall()
1312     {
1313         fall = true;
1314         frame = 0;
1315     }
1316 
1317     void reset_anim()
1318     {
1319         state = STATE.NO_ANIM;
1320     }
1321 
1322     void die(string by)
1323     {
1324         if (state != STATE.DIE_ANIM)
1325         {
1326             energy = 0.0;
1327             frame = 0;
1328             lives--;
1329             killed_by = by;
1330             state = STATE.DIE_ANIM;
1331         }
1332     }
1333 
1334     void rise()
1335     {
1336         reset_anim();
1337         energy = 100.0;
1338         fall = false;
1339         damaged_by = null;
1340         x = last_safe[0];
1341         y = last_safe[1];
1342         z = last_safe[2];
1343     }
1344 
1345     void start_left(GlobalState gs)
1346     {
1347         if ((!jump || cosm || underwater) && !fall)
1348         {
1349             dx -= SPEED;
1350         }
1351     }
1352 
1353     void stop_left(GlobalState gs)
1354     {
1355         if ((!jump || cosm || underwater) && !on_ice)
1356         {
1357             if (dx < 0.0) dx += SPEED;
1358             if (abs(dx) < 0.01) dx = 0.0;
1359         }
1360     }
1361 
1362     void
1363     start_right(GlobalState gs)
1364     {
1365         if ((!jump || cosm || underwater) && !fall)
1366         {
1367             dx += SPEED;
1368         }
1369     }
1370 
1371     void stop_right(GlobalState gs)
1372     {
1373         if ((!jump || cosm || underwater) && !on_ice)
1374         {
1375             if (dx > 0.0) dx -= SPEED;
1376             if (abs(dx) < 0.01) dx = 0.0;
1377         }
1378     }
1379 
1380     void start_jump(GlobalState gs)
1381     {
1382         if (underwater)
1383         {
1384             if (!cosm) dy = JUMP_V;
1385             else dy = MARS_JUMP_V;
1386             search_surface = false;
1387             jump = true;
1388         }
1389     }    
1390 
1391     void change_costume(GlobalState gs)
1392     {
1393         cosm = !cosm;
1394     }
1395 
1396     override void load(string[string] s)
1397     {
1398         if ("dizzy-x" in s)
1399             x = s["dizzy-x"].to!(float);
1400         else
1401             x = def_x;
1402             
1403         if ("dizzy-y" in s)
1404             y = s["dizzy-y"].to!(float);
1405         else
1406             y = def_y;
1407             
1408         if ("dizzy-z" in s)
1409             z = s["dizzy-z"].to!(float);
1410         else
1411             z = def_z;
1412 
1413         if ("dizzy-last-safe-x" in s)
1414             last_safe[0] = s["dizzy-last-safe-x"].to!(float);
1415         else
1416             last_safe[0] = def_x;
1417             
1418         if ("dizzy-last-safe-y" in s)
1419             last_safe[1] = s["dizzy-last-safe-y"].to!(float);
1420         else
1421             last_safe[1] = def_y;
1422             
1423         if ("dizzy-last-safe-z" in s)
1424             last_safe[2] = s["dizzy-last-safe-z"].to!(float);
1425         else
1426             last_safe[2] = def_z;
1427 
1428         if ("dizzy-dx" in s)
1429             dx = s["dizzy-dx"].to!(float);
1430         else
1431             dx = 0.0;
1432             
1433         if ("dizzy-dy" in s)
1434             dy = s["dizzy-dy"].to!(float);
1435         else
1436             dy = 0.0;
1437 
1438         jump = ("dizzy-jump" in s) !is null;
1439 
1440         if ("dizzy-energy" in s)
1441             energy = s["dizzy-energy"].to!(float);
1442         else
1443             energy = 100.0;
1444 
1445         if ("dizzy-lives" in s)
1446             lives = s["dizzy-lives"].to!(int);
1447         else
1448             lives = 3;
1449 
1450         if (lives > 3) lives = 3;
1451         if (energy > 100.0) energy = 100.0;
1452 
1453         if ("dizzy-frame" in s)
1454             frame = s["dizzy-frame"].to!(long);
1455 
1456         if ("dizzy-stunning" in s)
1457             stunning = s["dizzy-stunning"].to!(long);
1458         else
1459             stunning = 0;
1460     }
1461 
1462     override void save(ref string[string] s)
1463     {
1464         s["dizzy-x"] = x.to!(string);
1465         s["dizzy-y"] = y.to!(string);
1466         s["dizzy-z"] = z.to!(string);
1467 
1468         s["dizzy-dx"] = dx.to!(string);
1469         s["dizzy-dy"] = dy.to!(string);
1470 
1471         s["dizzy-last-safe-x"] = last_safe[0].to!(string);
1472         s["dizzy-last-safe-y"] = last_safe[1].to!(string);
1473         s["dizzy-last-safe-z"] = last_safe[2].to!(string);
1474 
1475         if (jump)
1476         {
1477             s["dizzy-jump"] = "yes";
1478             s["dizzy-frame"] = frame.to!(string);
1479         }
1480 
1481         if (energy < 100.0)
1482             s["dizzy-energy"] = energy.to!(string);
1483 
1484         if (lives < 3)
1485             s["dizzy-lives"] = lives.to!(string);
1486 
1487         if (stunning > 0)
1488             s["dizzy-stunning"] = stunning.to!(string);
1489     }    
1490 }